﻿; (function () {

    'use strict';
    window.SudokuGrid = {

        selectedCellIndices: null,
        similarCellsIndices: [],
        errorCellsIndices: [],

        checkErrorCellsIndices: [],

        /// <field type='GridModel'></field>
        gridModel: null,

        /// <field type='GridModel'></field>
        solvedGridModel: null,

        /// <field type='UndoRedoManager'></field>
        undoRedoManager: null,

        matchEnded: false,

        hintsProvider: new HintsProvider(new Solver(Strategies.getAll())),

        init: function () {
            EVENTBROKER.keyTopic.on(TOPIC_EVENTS.key, this.onKey.bind(this));
            EVENTBROKER.gridTopic.on(TOPIC_EVENTS.gridOnCellSelected, this.onCellSelected.bind(this));
            EVENTBROKER.matchTopic.on(TOPIC_EVENTS.matchHelpRequest, this.onMatchHelp.bind(this));
            EVENTBROKER.matchTopic.on(TOPIC_EVENTS.matchHintApply, this.onMatchHintApply.bind(this));
        },

        loadNewGrid: function (gridModel, solvedGridModel) {
            this.gridModel = gridModel;
            this.solvedGridModel = solvedGridModel;
            this.undoRedoManager = new UndoRedoManager(ObjectSerializer.serialize(gridModel));
            this.selectedCellIndices = null;
            this.matchEnded = false;
            this._updateGrid(gridModel);
        },

        onCellSelected: function (ev, cellIndices) {
            if (this.matchEnded)
                return;

            this.selectedCellIndices = cellIndices;
            EVENTBROKER.menuTopic.trigger(TOPIC_EVENTS.menuGo, "menu-play-keyboard");
            this._render();
        },

        onKey: function (ev, key) {

            this.handleOnNumKey(ev, key);

            if (key == "erease") {
                this._updateCellValue(0);
            }

            if (key == "undo") {
                var gridModelStr = this.undoRedoManager.undo();
                if (gridModelStr) {
                    this._updateGrid(ObjectSerializer.deserializeRoot(gridModelStr));
                }
            }

            if (key == "redo") {
                var gridModelStr = this.undoRedoManager.redo();
                if (gridModelStr) {
                    this._updateGrid(ObjectSerializer.deserializeRoot(gridModelStr));
                }
            }
        },

        onMatchHelp: function (ev, helpType) {

            if (helpType == "check") {
                this.helpCheck(ev, { glowWhenOk: true });
            }
            else if (helpType == "reveal-candidates") {
                HelpProvider.fillCandidates(this.gridModel);
                this.undoRedoManager.addState(ObjectSerializer.serialize(this.gridModel));

                this._render();
                var emptyCellsIndices = GridHelper.getCellsOfValue(this.gridModel, 0).map(function (cellModel) { return cellModel.indices; });
                EVENTBROKER.gridTopic.trigger(TOPIC_EVENTS.gridFlashGlowCells, [emptyCellsIndices]);
            }
            else if (helpType == "hint") {
                var checkResult = this.helpCheck(ev, { glowWhenOk: false });
                if (checkResult) {
                    var hintResult = this.hintsProvider.getHint(this.gridModel);
                    this.undoRedoManager.addState(ObjectSerializer.serialize(this.gridModel));
                    this.selectedCellIndices = null;
                    this._render();
                    EVENTBROKER.matchTopic.trigger(TOPIC_EVENTS.matchHintResult, hintResult);
                }
            }
        },

        helpCheck: function (ev, o) {
            var deltaCells = HelpProvider.check(this.gridModel, this.solvedGridModel);
            if (deltaCells.length > 0) {
                this.checkErrorCellsIndices = deltaCells.map(function (cellModel) { return cellModel.indices; });
                this.selectedCellIndices = null;
                this._render();
                this.glowFilledCells();
                return false;
            }
            else if (o.glowWhenOk) {
                this.glowFilledCells();
            }
            return true;
        },

        glowFilledCells: function () {
            var allCellsIndices = GridHelper.getFilledCells(this.gridModel).map(function (cellModel) { return cellModel.indices; });
            EVENTBROKER.gridTopic.trigger(TOPIC_EVENTS.gridFlashGlowCells, [allCellsIndices]);
        },

        onMatchHintApply: function (ev) {
            this.hintsProvider.applyHint(this.gridModel);
            this.undoRedoManager.addState(ObjectSerializer.serialize(this.gridModel));
            this._render();

            if (!this.matchEnded)
                EVENTBROKER.menuTopic.trigger(TOPIC_EVENTS.menuGo, "menu-play-help");
        },

        handleOnNumKey: function (ev, key) {
            var input = key;
            var isCandidateInput = CmdArgsHelper.IsSwitchArgs(key);
            if (isCandidateInput) {
                var args = CmdArgsHelper.GetSwitchArgsValue(key);
                input = args.key;
            }

            for (var i = 1; i < 10; i++) {
                if (i == input) {
                    this._updateCellValue(i, isCandidateInput);
                    break;
                }
            }
        },

        _triggerUpdateKeyStates: function () {
            var undoState = this.undoRedoManager.canUndo ? "-on" : "-off";
            EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keyDisable, "undo" + undoState);

            var redoState = this.undoRedoManager.canRedo ? "-on" : "-off";
            EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keyDisable, "redo" + redoState);

            if (this.selectedCellIndices != null) {
                var selectedCell = this.gridModel.getCell(this.selectedCellIndices);
                var editionKeyState = !selectedCell.isBase ? "-on" : "-off";
                EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keyDisable, "num" + editionKeyState);
                EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keyDisable, "clear" + editionKeyState);

                for (var i = 1; i < 10; i++) {
                    if (selectedCell.candidates.has[i]) {
                        EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keySwitch, "num" + i + "-on");
                    }
                    else {
                        EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keySwitch, "num" + i + "-off");
                    }
                }
            }
            else {
                EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keyDisable, "num" + "-off");
                EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keyDisable, "clear" + "-off");

                for (var i = 1; i < 10; i++) {
                    EVENTBROKER.keyTopic.trigger(TOPIC_EVENTS.keySwitch, "num" + i + "-off");

                }
            }
        },

        _updateGrid: function (gridModel) {
            this.gridModel.copy(gridModel);
            this._clearHints();
            this._clearPersistedStatesIndices();
            this._render();
        },

        _render: function () {
            this._triggerUpdateKeyStates();
            this._updateComputedStatesIndices();
            EVENTBROKER.gridTopic.trigger(TOPIC_EVENTS.gridFill, this);
        },

        _updateCellValue: function (value, isCandidate) {
            if (this.selectedCellIndices == null)
                return;

            var selectedCellModel = this.gridModel.getCell(this.selectedCellIndices);
            if (selectedCellModel.isBase)
                return;

            if (!isCandidate || value == 0) {
                selectedCellModel.setValue(value);
            }
            else {
                selectedCellModel.toggleCandidate(value);
            }

            SolvingHelper.removeDependingCandidates(selectedCellModel, this.gridModel);
            this.undoRedoManager.addState(ObjectSerializer.serialize(this.gridModel));

            this._clearHints();
            this._clearPersistedStatesIndicesOnIndices(this.selectedCellIndices);
            this._render();
        },

        _clearHints: function () {
            EVENTBROKER.matchTopic.trigger(TOPIC_EVENTS.matchHintClear, null);
        },

        _clearPersistedStatesIndices: function () {
            this.checkErrorCellsIndices.clear();
        },

        _clearPersistedStatesIndicesOnIndices: function (cellIndices) {
            this.checkErrorCellsIndices.removeWithComparer(cellIndices, IndicesHelper.AreEqual);
        },

        _updateComputedStatesIndices: function () {
            this._updateErrorCellsIndices();
            if (this.selectedCellIndices == null) {
                this.similarCellsIndices = [];
                return;
            }

            var selectedCell = this.gridModel.getCell(this.selectedCellIndices);
            if (selectedCell.value === 0) {
                this.similarCellsIndices = [];
                return;
            }

            var sameValueCells = GridHelper.getCellsOfValue(this.gridModel, selectedCell.value);
            this.similarCellsIndices = sameValueCells.map(function (cellModel) { return cellModel.indices; });
        },

        _updateErrorCellsIndices: function () {
            this.errorCellsIndices = GridHelper.getErrorsCells(this.gridModel).map(function (cellModel) { return cellModel.indices; });
        },

        resetState: function () {
        }
    };

    window.SudokuGrid.init();

}());
